﻿using System;
using System.Collections.Generic;
using Firefly.Framework.ContentManagement;
using Firefly.Framework.Graphics;
using Firefly.Framework.Input;
using Firefly.Framework.Windows.Forms;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace Firefly.Framework.Windows
{
    public class WindowsService : IUpdateable, IDrawable
    {
        public event EventHandler<KeyMapEventArgs> KeyDown;
        public event EventHandler<KeyMapEventArgs> KeyUp;
        public event EventHandler<MouseButtonMapEventArgs> Click;
        public event EventHandler<MouseButtonMapEventArgs> DoubleClick;
        public event EventHandler<MouseButtonMapEventArgs> MouseUp;
        public event EventHandler<MouseButtonMapEventArgs> MouseDown;
        public event EventHandler<MouseScrollWheelMapEventArgs> MouseScroll;

        private Control _focusedControl;
        private string _themeName;

        public SpriteBatch SpriteBatch { get; set; }
        public string LayoutPath { get; set; }
        public Control Focused
        {
            get { return _focusedControl; }
            set
            {
                if (_focusedControl != null)
                {
                    if (_focusedControl != value)
                    {
                        _focusedControl = value;
                    }
                }
                else
                {
                    _focusedControl = value;
                }
            }
        }
        public Clipboard Clipboard { get; private set; }
        public string ThemePath { get; set; }
        public string ThemeName
        {
            get { return _themeName; }
            set
            {
                if (!string.IsNullOrEmpty(_themeName))
                {
                    ThemePath.Remove(ThemePath.Length - _themeName.Length + 2);
                }

                _themeName = value;
                ThemePath += _themeName + "\\";
            }
        }
        public Theme Theme { get; private set; }
        public Desktop Desktop { get; private set; }

        public Control FindComponent(string name)
        {
            return Desktop.FindComponent(name);
        }

        public Control FindComponent(Vector2 position)
        {
            return Desktop.FindComponent(position);
        }

        public void DrawRectangle(Rectangle rect, Color color)
        {
            SpriteBatch.Draw(Theme.Pixel, rect, color);
        }

        #region Threadsafe Singleton

        public static WindowsService Instance
        {
            get { return Nested._instance; }
        }

        private class Nested
        {
            internal static readonly WindowsService _instance = new WindowsService();

            static Nested() { }
        }

        private WindowsService()
        {
            Enabled = true;
            UpdateOrder = 0;
            Visible = true;
            DrawOrder = int.MaxValue;
            SpriteBatch = GraphicsService.Instance.SpriteBatch;
            Desktop = new Desktop();
            LayoutPath = "Windows\\";
            ThemePath = "Windows\\Themes\\";
            ThemeName = "Default";
            Clipboard = new Clipboard();
        }

        #endregion

        #region IUpdateable Members

        public event EventHandler EnabledChanged;
        public event EventHandler UpdateOrderChanged;

        private bool _enabled;
        private int _updateOrder;

        public bool Enabled
        {
            get { return _enabled; }
            set
            {
                if (value != _enabled)
                {
                    _enabled = value;
                    NotifyEnabledChanged();
                }
            }
        }
        public int UpdateOrder
        {
            get { return _updateOrder; }
            set
            {
                if (value != _updateOrder)
                {
                    _updateOrder = value;
                    NotifyUpdateOrderChanged();
                }
            }
        }

        protected void NotifyEnabledChanged()
        {
            EventHandler handler = EnabledChanged;

            if (handler != null)
            {
                handler(this, new EventArgs());
            }
        }

        protected void NotifyUpdateOrderChanged()
        {
            EventHandler handler = UpdateOrderChanged;

            if (handler != null)
            {
                handler(this, new EventArgs());
            }
        }
        
        public void Update(GameTime gameTime)
        {
            if (Enabled)
            {
                Desktop.Update(gameTime);
            }
        }

        #endregion

        #region IDrawable Members

        public event EventHandler DrawOrderChanged;
        public event EventHandler VisibleChanged;

        private int _drawOrder;
        private bool _visible;

        public int DrawOrder
        {
            get { return _drawOrder; }
            set
            {
                if (value != _drawOrder)
                {
                    _drawOrder = value;
                    NotifyDrawOrderChanged();
                }
            }
        }
        public bool Visible
        {
            get { return _visible; }
            set
            {
                if (value != _visible)
                {
                    _visible = value;
                    NotifyVisibleChanged();
                }
            }
        }

        protected void NotifyDrawOrderChanged()
        {
            EventHandler handler = DrawOrderChanged;

            if (handler != null)
            {
                handler(this, new EventArgs());
            }
        }

        protected void NotifyVisibleChanged()
        {
            EventHandler handler = VisibleChanged;

            if (handler != null)
            {
                handler(this, new EventArgs());
            }
        }

        public void Draw(GameTime gameTime)
        {
            if (Visible)
            {
                SpriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Deferred, SaveStateMode.SaveState);
                Desktop.Draw(gameTime);
                Theme.DrawCursor(gameTime);
                SpriteBatch.End();
            }
        }

        #endregion

        #region IHasContent Members

        public void LoadContent()
        {
            InitializeInputMaps();
            Theme = ContentService.Instance.Load<Theme>(ThemePath + ThemeName);
            Theme.LoadContent();
            Desktop.LoadContent();
            Desktop.ResumeLayout();
        }

        public void UnloadContent()
        {
            Desktop.UnloadContent();

            if (Theme != null)
            {
                Theme.UnloadContent();
            }
        }

        #endregion

        #region IInputProcessor Members

        public int HoldThreshold { get; set; }
        public List<KeyMap> KeyMaps { get; protected set; }
        public List<MouseButtonMap> MouseButtonMaps { get; protected set; }
        public MouseAxisMap MouseAxisMap { get; protected set; }
        public MouseScrollWheelMap MouseScrollWheelMap { get; protected set; }
        public List<GamePadButtonMap> GamePadButtonMaps { get; protected set; }
        public List<GamePadDPadMap> GamePadDPadMaps { get; protected set; }
        public List<GamePadTriggerMap> GamePadTriggerMaps { get; protected set; }
        public List<GamePadThumbStickMap> GamePadThumbStickMaps { get; protected set; }

        public virtual void InitializeInputMaps()
        {
            MouseAxisMap = new MouseAxisMap();
            MouseAxisMap.PositionChanged += new EventHandler<MouseAxisMapEventArgs>(ProcessMouseAxisMap);
            MouseButtonMaps = new List<MouseButtonMap>();
            MouseButtonMaps.Add(new MouseButtonMap(new MouseButton[] { MouseButton.LeftButton }));
            MouseButtonMaps[0].MapReleased += new EventHandler<MouseButtonMapEventArgs>(ProcessMouseButtonMapReleased);
            MouseButtonMaps[0].MapPressed += new EventHandler<MouseButtonMapEventArgs>(ProcessMouseButtonMapPressed);
            MouseButtonMaps[0].MapDoubleClicked += new EventHandler<MouseButtonMapEventArgs>(ProcessMouseButtonMapDoubleClicked);
            MouseScrollWheelMap = new MouseScrollWheelMap();
            MouseScrollWheelMap.PositionChanged += new EventHandler<MouseScrollWheelMapEventArgs>(ProcessMouseScrollWheelMap);
            int index = 0;
            KeyMaps = new List<KeyMap>();

            foreach (Keys k in Enum.GetValues(typeof(Keys)))
            {
                KeyMaps.Add(new KeyMap(new Keys[] { k }));
                KeyMaps[index].MapPressed += new EventHandler<KeyMapEventArgs>(ProcessKeyMapPressed);
                KeyMaps[index].MapReleased += new EventHandler<KeyMapEventArgs>(ProcessKeyMapReleased);
                KeyMaps[index].MapHeld += new EventHandler<KeyMapEventArgs>(ProcessKeyMapPressed);
                KeyMaps[index].RepeatRate = 100;
                index++;
            }
        }

        private void ProcessKeyMapPressed(object sender, KeyMapEventArgs e)
        {
            if (Enabled)
            {
                KeyMap map = sender as KeyMap;

                if (_focusedControl != null)
                {
                    _focusedControl.RaiseBubbleEvent(this, new BubbleEventArgs(BubbleEvent.KeyDown, map, e));
                }
                else
                {
                    NotifyKeyDown(map, e);
                }
            }
        }

        private void ProcessKeyMapReleased(object sender, KeyMapEventArgs e)
        {
            if (Enabled)
            {
                KeyMap map = sender as KeyMap;

                if (_focusedControl != null)
                {
                    _focusedControl.RaiseBubbleEvent(this, new BubbleEventArgs(BubbleEvent.KeyUp, map, e));
                }
                else
                {
                    NotifyKeyUp(map, e);
                }
            }
        }

        private void ProcessMouseButtonMapDoubleClicked(object sender, MouseButtonMapEventArgs e)
        {
            if (Enabled)
            {
                MouseButtonMap map = sender as MouseButtonMap;

                if (map != null)
                {
                    Control target = Desktop.FindComponent(InputService.Instance.MouseManager.CurrentAxisPosition);

                    if (target != null)
                    {
                        target.RaiseBubbleEvent(this, new BubbleEventArgs(BubbleEvent.DoubleClicked, map, e));
                        target.RaiseBubbleEvent(this, new BubbleEventArgs(BubbleEvent.MouseUp, map, e));
                    }
                    else
                    {
                        NotifyDoubleClick(map, e);
                        NotifyMouseUp(map, e);
                    }
                }
            }
        }

        private void ProcessMouseScrollWheelMap(object sender, MouseScrollWheelMapEventArgs e)
        {
            if (Enabled)
            {
                MouseScrollWheelMap map = sender as MouseScrollWheelMap;

                if (map != null)
                {
                    Control target = Desktop.FindComponent(InputService.Instance.MouseManager.CurrentAxisPosition);

                    if (target != null)
                    {
                        target.RaiseBubbleEvent(this, new BubbleEventArgs(BubbleEvent.MouseScroll, map, e));
                    }
                    else
                    {
                        NotifyMouseScroll(map, e);
                    }
                }
            }
        }

        private void ProcessMouseButtonMapReleased(object sender, MouseButtonMapEventArgs e)
        {
            if (Enabled)
            {
                MouseButtonMap map = sender as MouseButtonMap;

                if (map != null)
                {
                    Control target = Desktop.FindComponent(InputService.Instance.MouseManager.CurrentAxisPosition);

                    if (target != null)
                    {
                        target.RaiseBubbleEvent(this, new BubbleEventArgs(BubbleEvent.Clicked, map, e));
                        target.RaiseBubbleEvent(this, new BubbleEventArgs(BubbleEvent.MouseUp, map, e));
                    }
                    else
                    {
                        NotifyMouseClick(map, e);
                        NotifyMouseUp(map, e);
                    }
                }
            }
        }

        private void ProcessMouseButtonMapPressed(object sender, MouseButtonMapEventArgs e)
        {
            if (Enabled)
            {
                MouseButtonMap map = sender as MouseButtonMap;

                if (map != null)
                {
                    Control target = Desktop.FindComponent(InputService.Instance.MouseManager.CurrentAxisPosition);

                    if (target != null)
                    {
                        target.RaiseBubbleEvent(this, new BubbleEventArgs(BubbleEvent.MouseDown, map, e));
                    }
                    else
                    {
                        _focusedControl = null;
                        NotifyMouseDown(map, e);
                    }
                }
            }
        }

        private void ProcessMouseAxisMap(object sender, MouseAxisMapEventArgs e)
        {
            if (Enabled)
            {
                MouseAxisMap map = sender as MouseAxisMap;

                if (map != null)
                {
                    Control target = Desktop.FindComponent(map.CurrentPosition);

                    if (target != null)
                    {
                        target.RaiseBubbleEvent(this, new BubbleEventArgs(BubbleEvent.MouseMove, map, e));
                    }
                }
            }
        }

        private void NotifyKeyDown(KeyMap map, KeyMapEventArgs e)
        {
            EventHandler<KeyMapEventArgs> handler = KeyDown;

            if (handler != null)
            {
                handler(this, e);
            }
        }

        private void NotifyKeyUp(KeyMap map, KeyMapEventArgs e)
        {
            EventHandler<KeyMapEventArgs> handler = KeyUp;

            if (handler != null)
            {
                handler(this, e);
            }
        }

        private void NotifyMouseUp(MouseButtonMap map, MouseButtonMapEventArgs e)
        {
            EventHandler<MouseButtonMapEventArgs> handler = MouseUp;

            if (handler != null)
            {
                handler(this, e);
            }
        }

        private void NotifyMouseDown(MouseButtonMap map, MouseButtonMapEventArgs e)
        {
            EventHandler<MouseButtonMapEventArgs> handler = MouseDown;

            if (handler != null)
            {
                handler(this, e);
            }
        }

        private void NotifyMouseClick(MouseButtonMap map, MouseButtonMapEventArgs e)
        {
            EventHandler<MouseButtonMapEventArgs> handler = Click;

            if (handler != null)
            {
                handler(this, e);
            }
        }

        private void NotifyDoubleClick(MouseButtonMap map, MouseButtonMapEventArgs e)
        {
            EventHandler<MouseButtonMapEventArgs> handler = DoubleClick;

            if (handler != null)
            {
                handler(this, e);
            }
        }

        private void NotifyMouseScroll(MouseScrollWheelMap map, MouseScrollWheelMapEventArgs e)
        {
            EventHandler<MouseScrollWheelMapEventArgs> handler = MouseScroll;

            if (handler != null)
            {
                handler(this, e);
            }
        }

        #endregion

        #region IDisposableEx Members

        public bool Disposed { get; private set; }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool disposing)
        {
            if (!Disposed)
            {
                if (disposing)
                {
                    UnloadContent();
                }

                Disposed = true;
            }
        }

        ~WindowsService()
        {
            Dispose(false);
        }

        #endregion
    }
}